//--------------------------------------------------------------------------------------
// File: normalwater.cpp
//--------------------------------------------------------------------------------------
#include "DXUT.h"
#include "DXUTcamera.h"
#include <string>
#include <vector>
#include <map>

const PTSTR ENV_CUBE_TEXTURE = TEXT("grassenvmap1024.dds");
const PTSTR WATER_NORMAL_TEXTURE_0 = TEXT("wave0.dds");
const PTSTR WATER_NORMAL_TEXTURE_1 = TEXT("wave1.dds");
const PTSTR AIRPLANE_FILENAME = TEXT("airplane.x");
const PTSTR SKY_EFFECT_FILE_NAME = TEXT("skyeffect.fx");
const PTSTR WATER_EFFECT_FILE_NAME = TEXT("water.fx");
const PTSTR NOLIGHT_EFFECT_FILE_NAME = TEXT("nolight.fx");
const PTSTR WHITE_TEX_FILE_NAME = TEXT("whitetex.dds");
const std::string EMPTY_TEXTURE = std::string("emptyTexture");

struct Mtrl
{
	D3DXCOLOR ambient;
	D3DXCOLOR diffuse;
	D3DXCOLOR spec;
	float specPower;
};

struct DirLight
{
	D3DXCOLOR ambient;
	D3DXCOLOR diffuse;
	D3DXCOLOR spec;
	D3DXVECTOR3 dir;
};

struct NMapVertex
{
	D3DXVECTOR3 pos;
	D3DXVECTOR3 tangent;
	D3DXVECTOR3 binormal;
	D3DXVECTOR3 normal;
	D3DXVECTOR2 tex;
};

D3DVERTEXELEMENT9 NMapVertexElements[] = 
{
	{0, 0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
	{0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0},
	{0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0},
	{0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
	{0, 48, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
	D3DDECL_END()
};	

CFirstPersonCamera		g_camera;
IDirect3DTexture9*		g_whitetex;
ID3DXMesh*				g_skybox = NULL;
IDirect3DCubeTexture9*	g_skyTexture = NULL;
ID3DXEffect*			g_skyEffect = NULL;
D3DXHANDLE				g_hSkyTech = 0;
D3DXHANDLE				g_hWVP = 0;
D3DXHANDLE				g_hViewMatrix = 0;
D3DXHANDLE				g_hProjMatrix = 0;
D3DXHANDLE				g_hSkyTexture = 0;

ID3DXMesh*				g_water = NULL;
IDirect3DTexture9*		g_waterNormal0 = NULL;
IDirect3DTexture9*		g_waterNormal1 = NULL;
ID3DXEffect*			g_waterEffect = NULL;
D3DXHANDLE				g_hWaterTech;
D3DXHANDLE				g_hWaterWorld;
D3DXHANDLE				g_hWaterWorldInv;
D3DXHANDLE				g_hWaterWVP;
D3DXHANDLE				g_hWaterMtrl;
D3DXHANDLE				g_hWaterLight;
D3DXHANDLE				g_hWaterEyePosW;
D3DXHANDLE				g_hWaterWaveMapOffset0;
D3DXHANDLE				g_hWaterWaveMapOffset1;
D3DXHANDLE				g_hWaterWaveMap0;
D3DXHANDLE				g_hWaterWaveMap1;
D3DXHANDLE				g_hWaterEnvMap;
D3DXHANDLE				g_hWaterReflectMap;
D3DXVECTOR2				g_waveMapOffset0;
D3DXVECTOR2				g_waveMapOffset1;

ID3DXEffect*			g_nolightEffect;
D3DXHANDLE				g_nolightTech;
D3DXHANDLE				g_nolightWVP;
D3DXHANDLE				g_nolightTex;

ID3DXMesh*				g_airplane;
DWORD					g_airplaneNumMtrl;
D3DXMATRIX				g_airplaneWorld;
std::vector<std::string> g_airplaneTexutes;
std::map<std::string, IDirect3DTexture9*> g_airplaneTexuteMap;

ID3DXRenderToSurface*	g_reflectRTS;
IDirect3DTexture9*		g_reflectTexture;
IDirect3DSurface9*		g_reflectTopSurface;

// Methods 
void CreateSkyEffect(IDirect3DDevice9 *pd3dDevice);
void CreateWaterEffect(IDirect3DDevice9 *pd3dDevice);
void CreateNoLightEffect(IDirect3DDevice9 *pd3dDevice);
void CreateWaterMesh(IDirect3DDevice9 *pd3dDevice);
void CreateAirplane(IDirect3DDevice9 *pd3dDevice);
void CreateReflectRTS(IDirect3DDevice9 *pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc);
void UpdateWaveMapOffect(float fElapsedTime);
void UpdateAirplanePos(float fElapsedTime);
void DrawReflectToTexture(IDirect3DDevice9 *pd3dDevice);
void DrawSky(IDirect3DDevice9 *pd3dDevice);
void DrawAirplane(IDirect3DDevice9 *pd3dDevice);
void DrawWater(IDirect3DDevice9 *pd3dDevice);


//--------------------------------------------------------------------------------------
// Rejects any D3D9 devices that aren't acceptable to the app by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsD3D9DeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat,
	bool bWindowed, void* pUserContext )
{
	// Typically want to skip back buffer formats that don't support alpha blending
	IDirect3D9* pD3D = DXUTGetD3D9Object();
	if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
		AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING,
		D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
		return false;

	return true;
}


//--------------------------------------------------------------------------------------
// Before a device is created, modify the device settings as needed
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
	pDeviceSettings->d3d9.pp.MultiSampleType = D3DMULTISAMPLE_8_SAMPLES;
	return true;
}


//--------------------------------------------------------------------------------------
// Create any D3D9 resources that will live through a device reset (D3DPOOL_MANAGED)
// and aren't tied to the back buffer size
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D9CreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
	void* pUserContext )
{
	D3DXCreateBox(pd3dDevice, 10.0f, 10.0f, 10.0f, &g_skybox, NULL);
	D3DXCreateCubeTextureFromFile(pd3dDevice, ENV_CUBE_TEXTURE, &g_skyTexture);
	D3DXCreateTextureFromFile(pd3dDevice, WHITE_TEX_FILE_NAME, &g_whitetex);
	D3DXCreateTextureFromFile(pd3dDevice, WATER_NORMAL_TEXTURE_0, &g_waterNormal0);
	D3DXCreateTextureFromFile(pd3dDevice, WATER_NORMAL_TEXTURE_1, &g_waterNormal1);

	CreateAirplane(pd3dDevice);
	CreateNoLightEffect(pd3dDevice);
	CreateWaterMesh(pd3dDevice);
	CreateSkyEffect(pd3dDevice);
	CreateWaterEffect(pd3dDevice);

	g_camera.SetViewParams(&D3DXVECTOR3(0.0f, 20.0f, -20.0f), &D3DXVECTOR3(0.0f, 0.0f, 0.0f));
	return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3D9 resources that won't live through a device reset (D3DPOOL_DEFAULT) 
// or that are tied to the back buffer size 
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D9ResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
	void* pUserContext )
{
	CreateReflectRTS(pd3dDevice, pBackBufferSurfaceDesc);
	g_camera.SetProjParams(D3DX_PI / 4, pBackBufferSurfaceDesc->Width / (float)pBackBufferSurfaceDesc->Height,
		1.0f, 1000.0f);
	return S_OK;
}


//--------------------------------------------------------------------------------------
// Handle updates to the scene.  This is called regardless of which D3D API is used
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
	UpdateWaveMapOffect(fElapsedTime);
	UpdateAirplanePos(fElapsedTime);
	g_camera.FrameMove(fElapsedTime);
}


//--------------------------------------------------------------------------------------
// Render the scene using the D3D9 device
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9FrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
	HRESULT hr;

	DrawReflectToTexture(pd3dDevice);

	// Clear the render target and the zbuffer 
	V( pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB( 0, 45, 50, 170 ), 1.0f, 0 ) );

	// Render the scene
	if( SUCCEEDED( pd3dDevice->BeginScene() ) )
	{
		DrawSky(pd3dDevice);
		DrawWater(pd3dDevice);
		DrawAirplane(pd3dDevice);

		V( pd3dDevice->EndScene() );
	}
}


//--------------------------------------------------------------------------------------
// Handle messages to the application 
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
	bool* pbNoFurtherProcessing, void* pUserContext )
{
	g_camera.HandleMessages(hWnd, uMsg, wParam, lParam);
	return 0;
}


//--------------------------------------------------------------------------------------
// Release D3D9 resources created in the OnD3D9ResetDevice callback 
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9LostDevice( void* pUserContext )
{
	g_reflectRTS->Release();
	g_reflectTexture->Release();
	g_reflectTopSurface->Release();
}


//--------------------------------------------------------------------------------------
// Release D3D9 resources created in the OnD3D9CreateDevice callback 
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9DestroyDevice( void* pUserContext )
{
	g_skyEffect->Release();
	g_skybox->Release();
	g_skyTexture->Release();
	g_airplane->Release();
	g_water->Release();
	g_waterNormal0->Release();
	g_waterNormal1->Release();
	g_waterEffect->Release();
	g_nolightEffect->Release();
	for(auto iter = g_airplaneTexuteMap.begin(); iter != g_airplaneTexuteMap.end(); ++iter)
	{
		std::string	stdStr = iter->first;
		iter->second->Release();
	}
	g_airplaneTexuteMap.clear();
	g_airplaneTexutes.clear();
	g_whitetex->Release();
}


//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
INT WINAPI wWinMain( HINSTANCE, HINSTANCE, LPWSTR, int )
{
	// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

	// Set the callback functions
	DXUTSetCallbackD3D9DeviceAcceptable( IsD3D9DeviceAcceptable );
	DXUTSetCallbackD3D9DeviceCreated( OnD3D9CreateDevice );
	DXUTSetCallbackD3D9DeviceReset( OnD3D9ResetDevice );
	DXUTSetCallbackD3D9FrameRender( OnD3D9FrameRender );
	DXUTSetCallbackD3D9DeviceLost( OnD3D9LostDevice );
	DXUTSetCallbackD3D9DeviceDestroyed( OnD3D9DestroyDevice );
	DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
	DXUTSetCallbackMsgProc( MsgProc );
	DXUTSetCallbackFrameMove( OnFrameMove );

	// TODO: Perform any application-level initialization here

	// Initialize DXUT and create the desired Win32 window and Direct3D device for the application
	DXUTInit( true, true ); // Parse the command line and show msgboxes
	DXUTSetHotkeyHandling( true, true, true );  // handle the default hotkeys
	DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
	DXUTCreateWindow( L"EmptyProject" );
	DXUTCreateDevice( true, 640, 480 );

	// Start the render loop
	DXUTMainLoop();

	// TODO: Perform any application-level cleanup here

	return DXUTGetExitCode();
}

void CreateWaterMesh(IDirect3DDevice9 *pd3dDevice)
{
	ID3DXMesh *pTempWaterMesh;
	D3DXCreateMesh(2, 4, D3DXMESH_MANAGED, NMapVertexElements, pd3dDevice, &pTempWaterMesh);
	
	NMapVertex *pVertex;
	pTempWaterMesh->LockVertexBuffer(0, (void**)&pVertex);
	NMapVertex vertex;
	vertex.pos = D3DXVECTOR3(-128.0f, 0.0f, -128.0f);
	vertex.normal = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	vertex.tex = D3DXVECTOR2(0.0f, 0.0f);
	pVertex[0] = vertex;
	vertex.pos = D3DXVECTOR3(-128.0f, 0.0f, 128.0f);
	vertex.tex = D3DXVECTOR2(0.0f, 16.0f);
	pVertex[1] = vertex;
	vertex.pos = D3DXVECTOR3(128.0f, 0.0f, 128.0f);
	vertex.tex = D3DXVECTOR2(16.0f, 16.0f);
	pVertex[2] = vertex;
	vertex.pos = D3DXVECTOR3(128.0f, 0.0f, -128.0f);
	vertex.tex = D3DXVECTOR2(16.0f, 0.0f);
	pVertex[3] = vertex;
	pTempWaterMesh->UnlockVertexBuffer();

	WORD *pIndex;
	pTempWaterMesh->LockIndexBuffer(0, (void**)&pIndex);
	pIndex[0] = 0;
	pIndex[1] = 1;
	pIndex[2] = 2;
	pIndex[3] = 3;
	pIndex[4] = 0;
	pIndex[5] = 2;
	pTempWaterMesh->UnlockIndexBuffer();

	D3DXComputeTangentFrameEx(
		pTempWaterMesh, // Input mesh
		D3DDECLUSAGE_TEXCOORD, 0, // Vertex element of input tex-coords.  
		D3DDECLUSAGE_BINORMAL, 0, // Vertex element to output binormal.
		D3DDECLUSAGE_TANGENT, 0,  // Vertex element to output tangent.
		D3DDECLUSAGE_NORMAL, 0,   // Vertex element to output normal.
		0, // Options
		0, // Adjacency
		0.01f, 0.25f, 0.01f, // Thresholds for handling errors
		&g_water, // Output mesh
		0);

	pTempWaterMesh->Release();
}

void CreateAirplane(IDirect3DDevice9 *pd3dDevice)
{
	ID3DXBuffer *mtrlBuffer;
	D3DXLoadMeshFromX(AIRPLANE_FILENAME, D3DXMESH_MANAGED, pd3dDevice, NULL, &mtrlBuffer, NULL, &g_airplaneNumMtrl, &g_airplane);
	D3DXMATERIAL *mtrl = static_cast<D3DXMATERIAL*>(mtrlBuffer->GetBufferPointer());
	for(DWORD i = 0; i < g_airplaneNumMtrl; i++)
	{
		if(mtrl[i].pTextureFilename)
		{
			std::string fileNameStr(mtrl[i].pTextureFilename);
			IDirect3DTexture9 *airplanTexture;
			g_airplaneTexutes.push_back(fileNameStr);

			if(g_airplaneTexuteMap.find(fileNameStr) == g_airplaneTexuteMap.end())
			{
				D3DXCreateTextureFromFileA(pd3dDevice, fileNameStr.c_str(), &airplanTexture);
				g_airplaneTexuteMap[fileNameStr] = airplanTexture;
			}
		}
		else
		{
			g_airplaneTexutes.push_back(EMPTY_TEXTURE);
		}
	}
}

void CreateWaterEffect(IDirect3DDevice9 *pd3dDevice)
{
	ID3DXBuffer *compilationErrors = NULL;
	D3DXCreateEffectFromFile(pd3dDevice, WATER_EFFECT_FILE_NAME, NULL, NULL, D3DXSHADER_DEBUG, NULL, &g_waterEffect,
		&compilationErrors);
	if(compilationErrors)
	{
		MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0);
		return;
	}
	g_hWaterTech			= g_waterEffect->GetTechniqueByName("WaterTech");
	g_hWaterWorld			= g_waterEffect->GetParameterByName(0, "gWorld");
	g_hWaterWorldInv		= g_waterEffect->GetParameterByName(0, "gWorldInv");
	g_hWaterWVP				= g_waterEffect->GetParameterByName(0, "gWVP");
	g_hWaterMtrl			= g_waterEffect->GetParameterByName(0, "gMtrl");
	g_hWaterLight			= g_waterEffect->GetParameterByName(0, "gLight");
	g_hWaterEyePosW			= g_waterEffect->GetParameterByName(0, "gEyePosW");
	g_hWaterWaveMapOffset0	= g_waterEffect->GetParameterByName(0, "gWaveMapOffset0");
	g_hWaterWaveMapOffset1	= g_waterEffect->GetParameterByName(0, "gWaveMapOffset1");
	g_hWaterWaveMap0		= g_waterEffect->GetParameterByName(0, "gWaveMap0");
	g_hWaterWaveMap1		= g_waterEffect->GetParameterByName(0, "gWaveMap1");
	g_hWaterEnvMap			= g_waterEffect->GetParameterByName(0, "gEnvMap");
	g_hWaterReflectMap		= g_waterEffect->GetParameterByName(0, "gReflectMap");
}

void CreateSkyEffect(IDirect3DDevice9 *pd3dDevice)
{
	ID3DXBuffer *compilationErrors = NULL;
	D3DXCreateEffectFromFile(pd3dDevice, SKY_EFFECT_FILE_NAME, NULL, NULL, D3DXSHADER_DEBUG, NULL, &g_skyEffect, &compilationErrors);
	if(compilationErrors)
	{
		MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0);
		return;
	}
	g_hSkyTech		= g_skyEffect->GetTechniqueByName("SkyTech");
	g_hSkyTexture	= g_skyEffect->GetParameterByName(0, "gSkyTexture");
	g_hWVP			= g_skyEffect->GetParameterByName(0, "gWVP");
	g_hViewMatrix	= g_skyEffect->GetParameterByName(0, "gViewMatrix");
	g_hProjMatrix	= g_skyEffect->GetParameterByName(0, "gProjMatrix");
}

void DrawSky(IDirect3DDevice9 *pd3dDevice)
{
	g_skyEffect->SetTexture(g_hSkyTexture, g_skyTexture);
	g_skyEffect->SetMatrix(g_hWVP, &((*g_camera.GetViewMatrix()) * (*g_camera.GetProjMatrix())));
	g_skyEffect->SetMatrix(g_hViewMatrix, g_camera.GetViewMatrix());
	g_skyEffect->SetMatrix(g_hProjMatrix, g_camera.GetProjMatrix());
	g_skyEffect->SetTechnique(g_hSkyTech);
	g_skyEffect->Begin(NULL, 0);
	g_skyEffect->BeginPass(0);

	g_skybox->DrawSubset(0);

	g_skyEffect->EndPass();
	g_skyEffect->End();
}

void UpdateWaveMapOffect(float dt)
{
	g_waveMapOffset0 += D3DXVECTOR2(0.05f, 0.08f) * dt;
	g_waveMapOffset1 += D3DXVECTOR2(-0.02f, 0.1f) * dt;

	if(g_waveMapOffset0.x >= 1.0f || g_waveMapOffset0.x <= -1.0f)
		g_waveMapOffset0.x = 0.0f;
	if(g_waveMapOffset1.x >= 1.0f || g_waveMapOffset1.x <= -1.0f)
		g_waveMapOffset1.x = 0.0f;
	if(g_waveMapOffset0.y >= 1.0f || g_waveMapOffset0.y <= -1.0f)
		g_waveMapOffset0.y = 0.0f;
	if(g_waveMapOffset1.y >= 1.0f || g_waveMapOffset1.y <= -1.0f)
		g_waveMapOffset1.y = 0.0f;
}

void UpdateAirplanePos(float fElapsedTime)
{
	static float angle = 0.0f;
	angle += fElapsedTime * D3DX_PI / 20.0f;
	if(angle >=  D3DX_PI * 2.0f)
		angle = 0.0f;

	D3DXMATRIX T, R;
	D3DXMatrixTranslation(&T, 25.0f, 5.0f, 0.0f);
	D3DXMatrixRotationY(&R, angle);
	g_airplaneWorld = T * R;
}

void DrawWater(IDirect3DDevice9 *pd3dDevice)
{
	D3DXMATRIX world, view, proj, worldInv;
	D3DXMatrixIdentity(&world);
	D3DXMatrixIdentity(&worldInv);
	view = *g_camera.GetViewMatrix();
	proj = *g_camera.GetProjMatrix();

	Mtrl waterMtrl;
	waterMtrl.ambient   = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
	waterMtrl.diffuse   = D3DXCOLOR(0.26f, 0.23f, 0.3f, 0.90f);
	waterMtrl.spec      = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	waterMtrl.specPower = 64.0f;

	DirLight waterLight;
	waterLight.ambient = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f);
	waterLight.diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
	waterLight.spec    = D3DXCOLOR(0.7f, 0.7f, 0.7f, 1.0f);
	waterLight.dir = D3DXVECTOR3(1.0f, -1.0f , 1.0f);
	
	g_waterEffect->SetMatrix(g_hWaterWorld, &world);
	g_waterEffect->SetMatrix(g_hWaterWorldInv, &worldInv);
	g_waterEffect->SetMatrix(g_hWaterWVP, &(world * view * proj));
	g_waterEffect->SetMatrix(g_hWaterWVP, &(world * view * proj));
	g_waterEffect->SetValue(g_hWaterMtrl, &waterMtrl, sizeof(Mtrl));
	g_waterEffect->SetValue(g_hWaterLight, &waterLight, sizeof(DirLight));
	g_waterEffect->SetValue(g_hWaterEyePosW, g_camera.GetEyePt(), sizeof(D3DXVECTOR3));
	g_waterEffect->SetValue(g_hWaterWaveMapOffset0, &g_waveMapOffset0, sizeof(D3DXVECTOR2));
	g_waterEffect->SetValue(g_hWaterWaveMapOffset1, &g_waveMapOffset1, sizeof(D3DXVECTOR2));
	g_waterEffect->SetTexture(g_hWaterWaveMap0, g_waterNormal0);
	g_waterEffect->SetTexture(g_hWaterWaveMap1, g_waterNormal1);
	g_waterEffect->SetTexture(g_hWaterEnvMap, g_skyTexture);
	g_waterEffect->SetTexture(g_hWaterReflectMap, g_reflectTexture);

	g_waterEffect->SetTechnique(g_hWaterTech);
	g_waterEffect->Begin(0, 0);
	g_waterEffect->BeginPass(0);
	g_water->DrawSubset(0);
	g_waterEffect->EndPass();
	g_waterEffect->End();
}

void DrawAirplane(IDirect3DDevice9 *pd3dDevice)
{
	g_nolightEffect->SetTechnique(g_nolightTech);
	D3DXMATRIX view, proj;
	view = *g_camera.GetViewMatrix();
	proj = *g_camera.GetProjMatrix();

	g_nolightEffect->SetMatrix(g_nolightWVP, &(g_airplaneWorld * view * proj));
	
	g_nolightEffect->Begin(0, 0);
	g_nolightEffect->BeginPass(0);
	for(DWORD i = 0; i < g_airplaneNumMtrl; i++)
	{
		std::string texName = g_airplaneTexutes[i];
		if(texName != EMPTY_TEXTURE)
		{
			g_nolightEffect->SetTexture(g_nolightTex, g_airplaneTexuteMap[texName]);
		}
		else
		{
			g_nolightEffect->SetTexture(g_nolightTex, g_whitetex);
		}
		g_nolightEffect->CommitChanges();

		g_airplane->DrawSubset(i);
	}
	g_nolightEffect->EndPass();
	g_nolightEffect->End();
}

void CreateNoLightEffect(IDirect3DDevice9 *pd3dDevice)
{
	ID3DXBuffer *compilationErrors = NULL;
	D3DXCreateEffectFromFile(pd3dDevice, NOLIGHT_EFFECT_FILE_NAME, NULL, NULL, D3DXSHADER_DEBUG, NULL, &g_nolightEffect, &compilationErrors);
	if(compilationErrors)
	{
		MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0);
		return;
	}

	g_nolightTech	= g_nolightEffect->GetTechniqueByName("NoLightTech");
	g_nolightWVP	= g_nolightEffect->GetParameterByName(0, "gWVP");
	g_nolightTex	= g_nolightEffect->GetParameterByName(0, "gTex");
}

void CreateReflectRTS(IDirect3DDevice9 *pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc)
{
	D3DXCreateRenderToSurface(pd3dDevice, pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height,
		pBackBufferSurfaceDesc->Format, TRUE, D3DFMT_D24S8, &g_reflectRTS);

	D3DXCreateTexture(pd3dDevice, pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height, 4,
		D3DUSAGE_RENDERTARGET | D3DUSAGE_AUTOGENMIPMAP, 
		pBackBufferSurfaceDesc->Format, D3DPOOL_DEFAULT, &g_reflectTexture);

	g_reflectTexture->GetSurfaceLevel(0, &g_reflectTopSurface);
}

void DrawReflectToTexture(IDirect3DDevice9 *pd3dDevice)
{
	HRESULT hr;
	V(g_reflectRTS->BeginScene(g_reflectTopSurface, NULL));
	V(pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(0, 0, 0, 0), 1.0f, 0));

	D3DXMATRIX reflect, view, proj;
	view = *g_camera.GetViewMatrix();
	proj = *g_camera.GetProjMatrix();
	D3DXPLANE xzPlane(0.0f, 1.0f, 0.0f, 0.0f);
	D3DXMatrixReflect(&reflect, &xzPlane);

	V(g_nolightEffect->SetTechnique(g_nolightTech));
	view = *g_camera.GetViewMatrix();
	proj = *g_camera.GetProjMatrix();

	V(g_nolightEffect->SetMatrix(g_nolightWVP, &(g_airplaneWorld * reflect * view * proj)));

	V(g_nolightEffect->Begin(0, 0));
	V(g_nolightEffect->BeginPass(0));
	for(DWORD i = 0; i < g_airplaneNumMtrl; i++)
	{
		std::string texName = g_airplaneTexutes[i];
		if(texName != EMPTY_TEXTURE)
		{
			V(g_nolightEffect->SetTexture(g_nolightTex, g_airplaneTexuteMap[texName]));
		}
		else
		{
			V(g_nolightEffect->SetTexture(g_nolightTex, g_whitetex));
		}
		V(g_nolightEffect->CommitChanges());

		V(g_airplane->DrawSubset(i));
	}
	V(g_nolightEffect->EndPass());
	V(g_nolightEffect->End());
	
	V(g_reflectRTS->EndScene(D3DX_FILTER_NONE));
}